package cn.huaqingcheng.tianshu.core.auth.jwt;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import jakarta.annotation.PostConstruct;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Optional;

/**
 * JwtServiceImpl
 *
 * @see <a href="https://github.com/auth0/java-jwt/blob/master/EXAMPLES.md">java-jwt 的示例</a>
 */
@Component
@RequiredArgsConstructor
public class JwtServiceImpl implements JwtService {

    private final JwtProperties properties;

    private KeyFileProvider provider;

    private JWTVerifier accessVerifier;

    @PostConstruct
    public void init() throws Exception {
        provider = new KeyFileProvider(properties);
        accessVerifier = JWT.require(Algorithm.ECDSA256(provider))
                .acceptLeeway(properties.getAcceptLeeway())
                .build();
    }

    @Override
    public JwtToken issued(JwtPayload payload) {
        var config = switch (payload.getType()) {
            case ACCESS -> properties.getAccess();
            case REFRESH -> properties.getRefresh();
        };
        Long expires = config.getExpires();
        ChronoUnit unit = config.getExpiresUnit();

        String issuer = Optional.ofNullable(payload.getIssuer())
                .orElseGet(properties::getIssuer);

        Instant now = Instant.now();
        Instant expiresAt = now.plus(expires, unit);

        String token = JWT.create()
                .withIssuer(issuer)
                .withSubject(payload.getSubject())
                .withIssuedAt(now)
                .withExpiresAt(expiresAt)
                .withClaim("accountId", payload.getAccountId())
                .withClaim("type", String.valueOf(payload.getType()))
                .withKeyId(provider.getPrivateKeyId())
                .sign(Algorithm.ECDSA256(provider));

        return new JwtToken(token, expiresAt);
    }

    @Override
    public JwtPayload verifyRead(String token) {
        DecodedJWT jwt = accessVerifier.verify(token);

        String issuer = jwt.getIssuer();
        String subject = jwt.getSubject();
        Long accountId = jwt.getClaim("accountId").asLong();
        JwtPayload.TokenType type = Optional.ofNullable(jwt.getClaim("type").asString())
                .map(JwtPayload.TokenType::valueOf)
                .orElse(null);

        return new JwtPayload()
                .setIssuer(issuer)
                .setType(type)
                .setSubject(subject)
                .setAccountId(accountId);
    }

}
